package com.tarena.lbs.coupon.service.impl;

import com.alibaba.nacos.common.utils.CollectionUtils;
import com.github.pagehelper.PageInfo;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.tarena.lbs.activity.api.ActivityApi;
import com.tarena.lbs.activity.api.pojo.ActivityDto;
import com.tarena.lbs.activity.pojo.po.ActivityPO;
import com.tarena.lbs.base.common.utils.Asserts;
import com.tarena.lbs.base.protocol.exception.BusinessException;
import com.tarena.lbs.base.protocol.pager.PageResult;
import com.tarena.lbs.business.api.BusinessApi;
import com.tarena.lbs.business.api.pojo.BusinessDto;
import com.tarena.lbs.common.GlobalExceptionHandle.ServiceException;
import com.tarena.lbs.common.enums.PassportResultEnum;
import com.tarena.lbs.common.enums.Roles;
import com.tarena.lbs.common.principle.UserPrinciple;
import com.tarena.lbs.common.utils.AuthenticationContextUtils;
import com.tarena.lbs.common.utils.PageConverter;
import com.tarena.lbs.coupon.api.pojo.param.UserCouponsParam;
import com.tarena.lbs.coupon.dao.repository.CouponCodeRepository;
import com.tarena.lbs.coupon.dao.repository.CouponRepository;
import com.tarena.lbs.coupon.dao.repository.StockRepository;
import com.tarena.lbs.coupon.dao.repository.UserCouponsRepository;
import com.tarena.lbs.coupon.pojo.param.CouponParam;
import com.tarena.lbs.coupon.pojo.po.CouponCodePO;
import com.tarena.lbs.coupon.pojo.po.CouponPO;

import com.tarena.lbs.coupon.pojo.po.CouponStockPO;
import com.tarena.lbs.coupon.pojo.po.UserCouponsPO;
import com.tarena.lbs.coupon.pojo.query.CouponStockParam;
import com.tarena.lbs.coupon.pojo.query.UserCouponCodeQuery;
import com.tarena.lbs.coupon.pojo.query.UserCouponQuery;
import com.tarena.lbs.coupon.pojo.vo.CouponVO;
import com.tarena.lbs.coupon.pojo.vo.UserCouponsVO;
import com.tarena.lbs.coupon.service.CouponService;
import com.tarena.lbs.user.api.UserApi;
import com.tarena.lbs.user.api.pojo.dto.AdminDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;

import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@Slf4j
@Service
public class CouponServiceImpl implements CouponService {
    @Autowired
    private CouponRepository couponRepository;
    @Autowired
    private UserCouponsRepository userCouponsRepository;
    @DubboReference(timeout = 5000)
    private ActivityApi activityApi;
    @DubboReference
    private BusinessApi basicApi;
    @DubboReference
    private UserApi  userApi;
    @Autowired
    private StockRepository stockRepository;
    @Autowired
    private CouponCodeRepository couponCodeRepository;
    @Qualifier("redisTemplate")
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public PageResult<CouponVO> pageList(Integer Busid) {
//        PageResult<CouponPO> pageResult = couponRepository.pageList(Busid);
//        log.info("pagePO数据+"+pageResult);
//
//        //PO转换为VO
//        PageResult<CouponVO> result = new PageResult<>();
//        BeanUtils.copyProperties(pageResult, result);
        log.info("id:{}",Busid);
        PageInfo<CouponPO> pageResult = couponRepository.pageList(Busid);
        List<CouponPO> list = pageResult.getList();
        log.info("pagePO数据+"+list);
        List<CouponVO> result=new ArrayList<>();
        if (CollectionUtils.isNotEmpty(list)){
            result = list.stream().map(item -> {
                CouponVO couponVO = new CouponVO();
                BeanUtils.copyProperties(item, couponVO);
                return couponVO;
            }).collect(Collectors.toList());
        }
        return PageConverter.convert(pageResult, result);



    }

    @Override
    public void addCoupon(CouponParam couponParam) throws BusinessException {
        UserPrinciple userPrinciple = AuthenticationContextUtils.get();
        Asserts.isTrue(userPrinciple==null,
                new BusinessException("-2","用户信息不存在"));
        Asserts.isTrue(userPrinciple.getRole() != Roles.SHOP,
                new BusinessException("-2","用户不是商家"));
        Integer adminId =userPrinciple.getId(); // 5
        log.info("用户id"+adminId);
        Integer adminBusinessId = getAdminBusinessId(adminId);
        log.info("商家id"+adminBusinessId);  //



        //新增优惠券，封装优惠券PO
        CouponPO couponPO = assembleCouponPO(couponParam,  adminBusinessId);
        couponRepository.Insert(couponPO);
        //同步库存
        initCouponStock(couponPO);


    }

    private void initCouponStock(CouponPO couponPO) throws BusinessException {
        CouponStockParam param = new CouponStockParam();
        param.setCouponId(couponPO.getId());
        param.setBusinessId(couponPO.getBusinessId());
        param.setNum(couponPO.getMaxUsageLimit());
        Boolean result=stockRepository.addStock( param);
        Asserts.isTrue(!result,new BusinessException("-2","库存同步失败"));
    }



    private CouponPO assembleCouponPO(CouponParam couponParam, Integer adminBusinessId) throws BusinessException {
        CouponPO couponPO = new CouponPO();
        //能拷贝的先拷贝
        BeanUtils.copyProperties(couponParam, couponPO);
        //设置商家id
        couponPO.setBusinessId(adminBusinessId);
        //设置优惠券状态， 0待激活，1待使用，2过期
        couponPO.setStatus(1);
        //优惠券管理状态，0启用，1禁用
        couponPO.setEnableStatus(0);
        //创建和更新时间
        couponPO.setCreateAt(new Date());
        couponPO.setUpdateAt(new Date());
        //入参携带的startDate和endDate 是字符串"2024-10-10 12:00:00"
        try {
            Date start= DateUtils.parseDate(couponParam.getStartDate(), "yyyy-MM-dd HH:mm:ss");
            Date end= DateUtils.parseDate(couponParam.getEndDate(), "yyyy-MM-dd HH:mm:ss");
            couponPO.setStartDate(start);
            couponPO.setEndDate(end);
        } catch (ParseException e) {
            throw new BusinessException("-2","入参字符串日期格式不正确");
        }
        return couponPO;
    }

    private Integer getAdminBusinessId(Integer adminId) throws BusinessException {
       AdminDto dto = userApi.getAdminById(adminId);
        Asserts.isTrue(dto==null,
                new BusinessException("-2","管理员信息不存在"));
        return dto.getId();
    }

    @Override
    public CouponVO detail(Integer id) {
        CouponPO couponPO = couponRepository.selectById(id);
        if (couponPO == null) {
            throw new ServiceException(PassportResultEnum.COUPON_NOT_EXIST);
            // 或者选择返回 null / 空对象，根据业务需求决定
            // return null;
        }
        CouponVO couponVO = new CouponVO();
        BeanUtils.copyProperties(couponPO, couponVO);
        return couponVO;
    }

    @Override
    public void receiveCoupon(UserCouponsParam param) throws BusinessException {
        UserPrinciple userPrinciple = AuthenticationContextUtils.get();
        if (userPrinciple == null) throw new ServiceException(PassportResultEnum.USER_NOT_LOGIN);
        ActivityDto activityById = activityApi.getActivityById(param.getActivityId());
        if (activityById==null){
            throw new ServiceException(PassportResultEnum.ACTIVITY_NOT_EXIST);
        }
        ActivityPO activityPO =new ActivityPO();
        BeanUtils.copyProperties(activityById,activityPO);
        CouponPO  couponPO = couponRepository.selectById(param.getCouponId());
        Integer userId = userPrinciple.getId();
        //检查领取优惠券的用户的身份，活动,优惠券是否存在
        if (userId==param.getUserId() && activityById!=null && couponPO!=null){
        doReceiveCoupon(param,userId,activityPO,couponPO);
        }else{
            throw new ServiceException(PassportResultEnum.BIND_ERROR);
        }
    }


    private void doReceiveCoupon(UserCouponsParam param, Integer userId, ActivityPO activity, CouponPO coupon) throws BusinessException {

        //T1 T2 T3 T4 T5
        //T1 1 查询充足 足
        //T2 1 查询充足 足
        //T1 2 写数据
        //T2 2 写数据
        //T1 3 减库存(-1) 剩余0
        //T2 3 也减库存(-1) 剩余-1
        //1.读取当前优惠券库存数量 判断是否充足 每次只需要领取一张
        Boolean tryLock=false;
        ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
        String lockKey = "receive:coupon:" + coupon.getId();
        String code = UUID.randomUUID().toString();
        //第一次进来抢锁 不需要等待 第二次重抢 第三次重抢需要等待1秒钟
        int count=0;
        do {
            //给线程一个重抢上3次 重抢抢不到就别抢了
            Asserts.isTrue(count>3,new BusinessException("-2","优惠券领取失败"));
            if (count>0){
                //当前线程 已经不是第一次抢锁了 等1秒
                try{
                    Thread.sleep(1000);
                }catch (InterruptedException e){
                    log.error("线程等待异常",e);
                }
            }
            Boolean enough = stockIsEnough(coupon.getId());
            //如果库存已经没有了 ==0 方法到这就结束了
            Asserts.isTrue(!enough, new BusinessException("-2", "优惠券库存不足"));
            //1.抢锁 有key value 操作redis setnx是string类型
            //SETNX key value EX 5 原子级
            log.info("库存充足开始抢锁,key:{},value:{}",lockKey,code);
            tryLock = opsForValue.setIfAbsent(lockKey, code, 5, TimeUnit.SECONDS);
            count++;
        }while (!tryLock);
        //释放锁一定要在finally
        try {
            log.info("抢锁成功,开始领取优惠券");
            //2.创建一个可领取的优惠券码 返回券码code编码
            String couponCode = createCouponCode(coupon);
            //3.记录一下领取信息
            createUserReceiveCouponInfo(param, userId, activity, coupon, couponCode);
            //4.扣减库存 扣减那个优惠券库存
            reduceStock(coupon.getId());
        }catch (Exception e){
            throw e;
        }finally {
            log.info("释放锁");
            //封装使用lua脚本
            String value = opsForValue.get(lockKey);
            if (value!=null&&value.equals(code)){
                log.info("锁标识正确 可以释放");
                redisTemplate.delete(lockKey);
            }
        }
    }

    private void reduceStock(Integer id) throws BusinessException {
       int  result = stockRepository.delStock(id);
        //保持强一致性 2个服务调用如果库存扩建失败 需要将上述的写操作都回滚
        Asserts.isTrue(result<=0,new BusinessException("-2","库存扣减失败"));
    }

    private void createUserReceiveCouponInfo(UserCouponsParam param, Integer userId, ActivityPO activity, CouponPO coupon, String couponCode) {
        UserCouponsPO userCouponsPO=new UserCouponsPO();
        //2.封装好属性
        userCouponsPO.setCouponValue(coupon.getDiscountValue());//优惠券面额
        userCouponsPO.setCreateAt(new Date());
        userCouponsPO.setUpdateAt(userCouponsPO.getCreateAt());
        userCouponsPO.setUserId(userId);
        userCouponsPO.setCouponId(coupon.getId());
        userCouponsPO.setCouponCode(couponCode);
        userCouponsPO.setStatus(0);//没有用到 用户领取记录的状态 0表示不空
        userCouponsPO.setShopId(param.getShopId()==null?0:param.getShopId());
        userCouponsPO.setActivityId(activity.getId());
        userCouponsPO.setReceiveChannel(param.getReceiveChannel()==null?0:param.getReceiveChannel());
        userCouponsPO.setCouponType(coupon.getCouponType());
        userCouponsPO.setOrderNo("");//领取记录 没有订单编号
        userCouponsPO.setUsedTime(null);//还没有使用 没有使用时间
        //3.保存到数据库 新增
        userCouponsRepository.Insert(userCouponsPO);
    }

    private String createCouponCode(CouponPO coupon) {
        CouponCodePO couponCodePO=new CouponCodePO();
        couponCodePO.setCreateAt(new Date());
        couponCodePO.setUpdateAt(couponCodePO.getCreateAt());
        couponCodePO.setStatus(1);//0未领取 1已领取 2已过期
        couponCodePO.setCouponId(coupon.getId());
        couponCodePO.setBusinessId(coupon.getBusinessId());
        //手动创建code编码
        String code=UUID.randomUUID().toString().replaceAll("-", "");
        couponCodePO.setCouponCode(code);
        //2.写入数据库
        couponCodeRepository.Insert(couponCodePO);
        //3.返回code
        return code;
    }

    private Boolean stockIsEnough(Integer id) {
        CouponStockPO couponStockPO =stockRepository.selectById(id);
        if (couponStockPO==null){
            return false;
        }
        return couponStockPO.getNum()>0;

    }


    @Override
    public PageResult<UserCouponsVO> receiveList(UserCouponQuery couponQuery) {
        PageResult<UserCouponsPO> pageResult = userCouponsRepository.pageList(couponQuery);
        PageResult<UserCouponsVO> result = new PageResult<>();
        BeanUtils.copyProperties(pageResult,result);
        return result;
    }

    @Override
    public UserCouponsVO receiveDetail(UserCouponCodeQuery query) {
        UserCouponsPO userCouponsPO = userCouponsRepository.selectUserCoupon(query);
        UserCouponsVO userCouponsVO = new UserCouponsVO();
        BeanUtils.copyProperties(userCouponsPO,userCouponsVO);
        return userCouponsVO;

    }

}
